/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.

    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/

    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews

    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/
package bloggate;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DownstreamBlogGateUtility 
{
	private static DownstreamBlogGateUtility singleton = null;
	private HttpClient client;
	private final Log logger = LogFactory.getLog(getClass());	
	private DBConnectionPool dbConnectionPool ;
	private String eTagQuery = "Select last_entry_id from bloggate where feed_name = ?";
	private String eTagUpdate = "Update bloggate set last_entry_id = ? where feed_name = ?";
	private String eTagInsert = "Insert into bloggate values(default, ?, ?)";
	
	public DownstreamBlogGateUtility(HashMap serviceMap, String jdbcConnectionURI, int httpTimeOut, String user, String password) throws Exception
	{
		client = new HttpClient();
		HttpConnectionManagerParams hcp = new HttpConnectionManagerParams();
		hcp.setSoTimeout(httpTimeOut);
		hcp.setConnectionTimeout(httpTimeOut);
		HttpConnectionManager connManager = client.getHttpConnectionManager();
		connManager.setParams(hcp);
		dbConnectionPool = DBConnectionPool.getInstance(jdbcConnectionURI, user, password);
		Iterator iter = serviceMap.keySet().iterator();
		Connection conn = dbConnectionPool.getConnection();
	    	PreparedStatement preparedStmt = null;
	    	ResultSet rs = null;
	    	PreparedStatement preparedStmt1 = null;
		try
		{
			while(iter.hasNext())
	    		{
	    			String serviceName = ((String) iter.next()).trim();
				if (serviceName == null || serviceName.trim().equals(""))
				{
					logger.warn("DownstreamBlogGateUtility: Service Name not defined");
					throw new Exception();
				}
				preparedStmt = dbConnectionPool.getPreparedStmt(conn, eTagQuery);
				preparedStmt.setString(1, serviceName);
				rs = preparedStmt.executeQuery();
				if (rs.next() == false)
				{
					preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, eTagInsert);
					preparedStmt1.setString(1, serviceName);
					preparedStmt1.setInt(2, 0);
					preparedStmt1.executeUpdate();
	        			preparedStmt1.close(); 
			    		preparedStmt1 = null;
				}
				rs.close(); 
				rs = null;
				preparedStmt.close(); 
				preparedStmt = null;
			}
		}
		catch (Exception e)
		{
			logger.error("Error starting UpstreamBlogGateUtility");
			e.printStackTrace();
			throw e;
		}
		finally
		{
			try 
			{ 
				if (rs != null)
				{
					rs.close();
					rs = null;
				}
				if (preparedStmt != null)
				{
					preparedStmt.close();
					preparedStmt = null;
				}
				if (preparedStmt1 != null)
				{
					preparedStmt1.close();
					preparedStmt1 = null;
				}
				if (conn != null)
				{
					conn.close();
					conn = null;
				}
			} 
			catch (Exception e) {}
		}
	}
	protected static DownstreamBlogGateUtility getInstance(HashMap serviceMap, String jdbcConnectionURI, int httpTimeOut, String user, String password) throws Exception
	{
		if (singleton == null)
		{
			singleton = new DownstreamBlogGateUtility(serviceMap, jdbcConnectionURI, httpTimeOut, user, password);
		}
		return singleton;
	}
	
    protected synchronized void enableDownStreamService(HashMap serviceMap, String rootBlogCacheUrlBase)
    {
    	Connection conn = null;
    	PreparedStatement preparedStmt = null;
    	PreparedStatement preparedStmt1 = null;
    	ResultSet rs = null;
    	GetMethod serviceGet = null;
    	String serviceURL = null;
    	String contentString = null;
    	try 
		{
			conn = dbConnectionPool.getConnection();
			Iterator iter = serviceMap.keySet().iterator(); 
	    	while(iter.hasNext())
	    	{
	    		String serviceName = ((String) iter.next()).trim();
				serviceURL = (String) serviceMap.get(serviceName);
				if (serviceName == null || serviceName.trim().equals(""))
				{
					logger.warn("DownstreamBlogGateUtility: Service Name not defined");
					throw new Exception();
				}
				preparedStmt = dbConnectionPool.getPreparedStmt(conn, eTagQuery);
				preparedStmt.setString(1, serviceName);
				rs = preparedStmt.executeQuery();
				Long oldEtag = 0L;
				if (rs.next() == true)
				{
					oldEtag = rs.getLong("last_entry_id");
				}
				rs.close(); rs = null;
				preparedStmt.close(); preparedStmt = null;
				serviceGet = new GetMethod(serviceURL);
				serviceGet.addRequestHeader(new Header("If-None-Match", String.valueOf(oldEtag)));
				client.executeMethod(serviceGet);
				if (serviceGet.getStatusCode() == HttpStatus.SC_OK) 
			    {
					Header eTagHeader = serviceGet.getResponseHeader("ETag");
					int newEtag = 0;
					if (eTagHeader != null)
					{
						newEtag = Integer.parseInt(eTagHeader.getValue());
					}
					else
					{
						logger.warn("Etag Not set in HTTP Header for Service "+serviceName);
						serviceGet.releaseConnection(); serviceGet = null;
						conn.close(); conn = null;
						return;
					}
					Header xtuListHeader = serviceGet.getResponseHeader("X-TU-List");
					if (xtuListHeader != null)
					{
		        		String tuCodeList = xtuListHeader.getValue();
						StringBuffer content = new StringBuffer();
						BufferedReader br = new BufferedReader(new InputStreamReader(serviceGet.getResponseBodyAsStream()));
						String readLine = br.readLine();
						while(readLine != null)
						{
							content.append(readLine).append("\n");
							readLine = br.readLine();
						}
						contentString = content.toString().trim();
						logger.info("Content Received at Bloggate: "+contentString);
						int postErrors = 0;
	    				if (contentString == null || contentString.equals(""))
	    				{
	    					logger.warn("Empty Content "+ content + " is not posted to root blog cache: "+serviceURL);
	    				}
	    				if (tuCodeList == null || tuCodeList.trim().equals(""))
	    				{
	    					logger.warn("tuCodeList empty. So not posted to root blog cache: "+serviceURL);
	    				}
    					postErrors = postContentToRootBlogCache(tuCodeList, serviceName, contentString, rootBlogCacheUrlBase);
	    				if (postErrors == 0)
	    				{
    						preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, eTagUpdate);
						preparedStmt1.setInt(1, newEtag);
						preparedStmt1.setString(2, serviceName);
	    					preparedStmt1.executeUpdate();
	    					preparedStmt1.close();
	    					preparedStmt1 = null;
	    				}
					}
					else
					{
						logger.warn("X-TU-List not set. So, not posted to Root blog Cache: "+serviceURL);
					}
			    }
				else if (serviceGet.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) 
				{
					logger.info("HttpStatus.SC_NOT_MODIFIED: "+serviceURL);
				}
				else if (serviceGet.getStatusCode() == HttpStatus.SC_REQUEST_TIMEOUT |
						serviceGet.getStatusCode() == HttpStatus.SC_GATEWAY_TIMEOUT |
						serviceGet.getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) 
				{
					logger.error("HTTP error while getting content from Service App: " +serviceURL + " Status code: "+ serviceGet.getStatusCode());
				}
				else 
				{
					logger.error("HTTP error while getting content from Service App " + serviceURL + " Status code: "+ serviceGet.getStatusCode());
				}
				serviceGet.releaseConnection(); serviceGet = null;
	    	}
		} 
		catch (Exception e)
		{
			logger.error("Error during Downstream GET from Service App: " +serviceURL + "  "+ e + " Content: "+ contentString);
			try 
			{
				if (serviceGet != null)
					logger.info(serviceGet.getResponseBodyAsStream());
			} catch (IOException e1) {}
			//e.printStackTrace();
		}
		finally
		{
			try 
			{ 
				if (rs != null)
				{
					rs.close();
					rs = null;
				}
				if (preparedStmt != null)
				{
					preparedStmt.close();
					preparedStmt = null;
				}
				if (preparedStmt1 != null)
				{
					preparedStmt1.close();
					preparedStmt1 = null;
				}
				if (conn != null)
				{
					conn.close();
					conn = null;
				}
				if (serviceGet != null)
				{
					serviceGet.releaseConnection();
					serviceGet = null;
				}
			} 
			catch (Exception e) {}
		}
    }
    
    private synchronized int postContentToRootBlogCache(String tuCodeList,
											    	     String serviceName,
											    	     String content,
											    	     String rootBlogCacheUrlBase)
    {
		PostMethod rootBlogCachePost = null;
		String feedURI = null;
		int postErrors = 0;
    	try 
		{
			StringBuffer xmlBody = new StringBuffer(); 
			xmlBody.append("<entry>\n<title>")
				   .append(serviceName)
				   .append("</title>\n<author>\n<name>null</name>\n</author>\n<id>null</id>\n<category>null</category>\n<content>\n")
				   .append(content)
				   .append("\n</content>\n</entry>\n");
			ByteArrayInputStream is = new ByteArrayInputStream(xmlBody.toString().getBytes());
			InputStreamRequestEntity requestEntity = new InputStreamRequestEntity(is);
			StringTokenizer st = new StringTokenizer(tuCodeList, ",");
			String url =  rootBlogCacheUrlBase + serviceName + "/";
			
			while(st.hasMoreTokens())
			{
				String tuCode = st.nextToken();
				feedURI = url + tuCode;
				rootBlogCachePost = new PostMethod(feedURI);
				rootBlogCachePost.setRequestEntity(requestEntity);
				rootBlogCachePost.addRequestHeader(new Header("User-Agent", "blogate"));
				client.executeMethod(rootBlogCachePost);
				if (rootBlogCachePost.getStatusCode() != HttpStatus.SC_OK)
				{
					logger.error("Failure in posting to " + feedURI + " HTTP Status code: "+rootBlogCachePost.getStatusCode() + " Content: "+content);
					postErrors++;
				}
				else
				{
					logger.info("Posted to " + feedURI);
				}
				rootBlogCachePost.releaseConnection(); rootBlogCachePost = null;
			}
		} 
		catch (Exception e) 
		{
			if(rootBlogCachePost != null)
			{
				rootBlogCachePost.releaseConnection();
				rootBlogCachePost = null;
			}
			logger.error("Failure during Downstream POST to Root Blog Cache: " +feedURI + " "+e + " Content: "+ content);
			postErrors++;
			//e.printStackTrace();
		}
		finally
		{
			try 
			{ 
				if (rootBlogCachePost != null)
				{
					rootBlogCachePost.releaseConnection();
					rootBlogCachePost = null;
				}
			} 
			catch (Exception e) {}
		}
		return postErrors;
    }
}
